#include "zen_vap_texture.h"
#include "zen_file.h"
#include "zen_number.h"
#include "zen_utils.h"
#include "zen_exception.h"

namespace Zen { namespace Vap {

	SharedTexture Texture::Create()
	{
		return SharedTexture(new Texture);
	}

	void Texture::set(std::shared_ptr<Image> image)
	{
		this->set(image->width(), image->height(), image->format(), image->data());
	}
#if ZEN_APP_GLES
	void Texture::set(size_t width, size_t height, ePixel format, void const * data)
	{
		musts(width < 0xffffff && height < 0xffffff, "");

#if 0 // fill
		if(width <= m_inner_size.w && height <= m_inner_size.h && format == m_format) {
			m_buffer.fill(0, 0, width, height, data);
			m_size = { (int32_t)width, (int32_t)height };
			m_range = { (float)width /(float)m_inner_size.w, (float)height /(float)m_inner_size.h };
			return;
		}
#endif

		if(format == ePixel::GA)
		{
			auto image = Image::Create(ePixel::RGBA, width, height, 0xff);
			image->fill(format, data);
			this->set(image);
		}
		else
		{
			m_format = format;
			m_size = { (int32_t)width, (int32_t)height };

#	if ZEN_GL_2D_ANY_SZ
			m_buffer.createWithData(width, height, GL::eFilter::Linear, format, data);
			m_range = { 1.0f, 1.0f };
			m_inner_size = m_size;
#	else
			auto w = Zen::NumMinPowerTwo((uint32_t)width);
			auto h = Zen::NumMinPowerTwo((uint32_t)height);
			
			if(w == width && h == height)
			{
				m_buffer.createWithData(width, height, GL::eFilter::Linear, format, data);

				m_range = { 1.0f, 1.0f };
				m_inner_size = m_size;
			}
			else
			{
				m_buffer.create(w, h, GL::eFilter::Linear, format);
				m_buffer.fill(0, 0, width, height, data);

				m_range = { (float)width /(float)w, (float)height /(float)h };
				m_inner_size = { (int32_t)w, (int32_t)h };
			}
#	endif
			
		}
	}
#elif ZEN_APP_METAL
	void Texture::set(size_t width, size_t height, ePixel format, void const * data)
	{
		musts(width < 0xffffff && height < 0xffffff, "");

		if(format == ePixel::GA)
		{
			auto image = Image::Create(ePixel::RGBA, width, height, 0xff);
			image->fill(format, data);
			this->set(image);
		}
		else if(format == ePixel::RGB)
		{
			auto image = Image::Create(ePixel::RGBA, width, height, 0xff);
			image->fill(format, data);
			this->set(image);
		}
		else
		{
			m_format = format;
			m_size = { (int32_t)width, (int32_t)height };

			m_buffer.create(width, height, m_format==ePixel::Grey, data, 1);
			m_range = { 1.0f, 1.0f };
			m_inner_size = m_size;
		}
	}
#endif


	TexturesCache * TexturesCache::Get()
	{
		static auto single = new TexturesCache;
		return single;;
	}
}}

/**
 default system texture
 */
namespace Zen { namespace Vap {

	TextureLoader * TextureLoader::Get()
	{
		static auto me = new TextureLoader;
		return me;
	}
	SharedTexture TextureLoader::loadTexture(const std::string & path)
	{
		auto ext = GetFilePathExtention(path);
		auto decoder = this->getImageDecoder(ext);
		if(!decoder)
		{
			decoder = this->getImageDecoder("");
			if(!decoder) return nullptr;
		}

		auto data = Zen::Utils::Get()->loadResource(path);
		if(data.empty())
		{
			data = Zen::Utils::Get()->loadDocument(path);
			if(data.empty()) return nullptr;
		}

		try
		{
			auto image = decoder->decode(data);
			auto texture = Texture::Create();
			texture->set(image);
			return texture;
		}
		catch(std::exception & e)
		{
			return nullptr;
		}
	}

	TextureLoader::TextureLoader()
	{
		this->setImageDecoder("", std::shared_ptr<ImageDecoder>(new ImageDefaultDecoder));
	}

	void TextureLoader::setImageDecoder(const std::string &extension, std::shared_ptr<ImageDecoder> decoder)
	{
		if(!decoder)
			m_image_decoders.erase(extension);
		else
			m_image_decoders[extension] = decoder;
	}
	std::shared_ptr<ImageDecoder> TextureLoader::getImageDecoder(std::string const & path)
	{
		for(auto & e : m_image_decoders) {
			auto & key = e.first;
			if(path.size() >= key.size()) {
				if(0 == ::memcmp(path.data()+(path.size()-key.size()), key.data(), key.size()))
				{
					return e.second;
				}
			}
		}
		return nullptr;
	}
	SharedTexture TextureLoader::getParticleTexture()
	{
		static SharedTexture texture;
		
		if(texture) return texture;

		int size = 256;
		float r = (float)size/2;

		auto image = Image::Create(ePixel::Grey, size, size);
		auto data = image->data();
		for(int i = 0; i < size; ++i)
		{
			float x2 = NumSquare(r-i);
			for(int j = 0; j < size; ++j)
			{
				float y2 = NumSquare(r-j);
				float v = (float)(sqrtf(x2+y2) / r);
				if(v > 0.99) data[0] = 0;
				else data[0] = (uint8_t)(255.f * (1-v));
				++data;

				// rgba
//				data[0] = data[1] = data[2] = 0xff;
//				if(v > 0.99) data[3] = 0;
//				else data[3] = (uint8_t)(254.f * (1-v));
//				data += 4;

			}
		}
		texture = Texture::Create();
		texture->set(image);
		return texture;
	}

	SharedTexture TextureLoader::getCrossTexture()
	{
		static SharedTexture texture;
		if(texture) return texture;

		int size = 256;
		int r = (size/2);

		auto image = Image::Create(ePixel::Grey, size, size);
		auto data = image->data();
		for(int i = 0; i < size; ++i)
		{
			int x = std::abs(r - i);
			for(int j = 0; j < size; ++j)
			{
				int y = std::abs(r - j);
				if(std::abs(x-y) > 8) *data = 0;
				else *data = 255;
				++data;
			}
		}
		texture = Texture::Create();
		texture->set(image);
		return texture;
	}
	SharedTexture TextureLoader::getCircleTexture()
	{
		static SharedTexture texture;
		if(texture) return texture;

		int size = 256;
		float r = (float)size/2;

		auto image = Image::Create(ePixel::Grey, size, size);
		auto data = image->data();

		for(int i = 0; i < size; ++i)
		{
			float x2 = NumSquare(r-i);
			for(int j = 0; j < size; ++j)
			{
				float y2 = NumSquare(r-j);
				float v = (float)(sqrtf(x2+y2) / r);
				if(v >= 1.f) *data = 0;
				else if(v > 0.9f) *data = (uint8_t)(254.f * NumSquare(10.f*(1.f-v)));
				else if(v > 0.8f) *data = (uint8_t)(254.f * NumSquare(10.f*(v-.8f)));
				else *data = 0;
				++data;
			}
		}
		texture = Texture::Create();
		texture->set(image);
		return texture;
	}

}}
